//
// Saru.Base.Math.matrix2.cs: 2 Dimensional matrices and operations
//
// Author:
//   Jan Van Sweevelt (vansweej@gmail.com)
//
// Copyright (C) 2007
// 

using System;

namespace Saru.Base.Math {

    public class Matrix2f {

	// Constructors

        // create identity matrix
	public Matrix2f() {
	    tuple = new float[4];
	    MakeIdentity();
	}

	// copy constructor
	public Matrix2f(Matrix2f M) {
	    tuple = new float[4];
	    tuple[0] = M.tuple[0];
	    tuple[1] = M.tuple[1];
	    tuple[2] = M.tuple[2];
	    tuple[3] = M.tuple[3];
	}

	// construct from floating points
	public Matrix2f(float M00, float M01, float M10, float M11) {
	    tuple = new float[4];
	    tuple[0] = M00;
	    tuple[1] = M01;
	    tuple[2] = M10;
	    tuple[3] = M11;
	}

	// construct from vectors
	// colums means:
	// true: vectors are colums of the matrix
	// false: vectors are rows of the matrix
	public Matrix2f(Vector2f V1, Vector2f V2, bool colums) {
	    tuple = new float[4];
	    if(colums) {
		tuple[0] = V1.X;
		tuple[1] = V2.X;
		tuple[2] = V1.Y;
		tuple[3] = V2.Y;		
	    }
	    else {
		tuple[0] = V1.X;
		tuple[1] = V1.Y;
		tuple[2] = V2.X;
		tuple[3] = V2.Y;		
	    }
	}

	// create a diagonal matrix
	public Matrix2f(float M00, float M11) {
	    tuple = new float[4];

	    MakeDiagonal(M00, M11);
	}

	// create a rotation matrix (positive angle - counterclockwise)
	public Matrix2f(float Angle) {
	    tuple = new float[4];

	    FromAngle(Angle);
	}

	public void MakeZero() {
	    tuple[0] = 0.0f;
	    tuple[1] = 0.0f;
	    tuple[2] = 0.0f;
	    tuple[3] = 0.0f;
	}

	public void MakeIdentity() {
	    tuple[0] = 1.0f;
	    tuple[1] = 0.0f;
	    tuple[2] = 0.0f;
	    tuple[3] = 1.0f;
	}

	public void MakeDiagonal(float M00, float M11) {
	    tuple[0] = M00;
	    tuple[1] = 0.0f;
	    tuple[2] = 0.0f;
	    tuple[3] = M11;
	}

	public void FromAngle(float Angle) {
	    tuple[0] = (float)System.Math.Cos(Angle);
	    tuple[2] = (float)System.Math.Sin(Angle);
	    tuple[1] = -tuple[2];
	    tuple[3] = tuple[0];
	}

       // accessors
	public float[] Matrix2 {
	    get {
		return tuple;
	    }

	    set {
		tuple = value;
	    }
	}

	public float Get(short Column, short Row) {
	    return tuple[Column + 2 * Row];
	}

	public void Set(float Value, short Column, short Row) {
	    tuple[Column + 2 * Row] = Value;
	}

	public Vector2f GetRow(short Row) {
	    int i0 = 2*Row;
	    return new Vector2f(tuple[i0], tuple[i0+1]);
	}

	public void SetRow(short Row, Vector2f V) {
	    int i0 = 2*Row;
	    tuple[i0] = V.X;
	    tuple[i0+1] = V.Y;
	}

	// comparison
	public static bool operator==(Matrix2f M1, Matrix2f M2) {
	    return M1.tuple == M2.tuple;
	}

	public static bool operator!=(Matrix2f M1, Matrix2f M2) {
	    return M1.tuple != M2.tuple;
	}

	public static bool operator<(Matrix2f M1, Matrix2f M2) {
	    return (M1.tuple[0] < M2.tuple[0] &&
		    M1.tuple[1] < M2.tuple[1] &&
		    M1.tuple[2] < M2.tuple[2] &&
		    M1.tuple[3] < M2.tuple[3]);
	}

	public static bool operator>(Matrix2f M1, Matrix2f M2) {
	    return (M1.tuple[0] > M2.tuple[0] &&
		    M1.tuple[1] > M2.tuple[1] &&
		    M1.tuple[2] > M2.tuple[2] &&
		    M1.tuple[3] > M2.tuple[3]);
	}

	public static bool operator<=(Matrix2f M1, Matrix2f M2) {
	    return (M1.tuple[0] <= M2.tuple[0] &&
		    M1.tuple[1] <= M2.tuple[1] &&
		    M1.tuple[2] <= M2.tuple[2] &&
		    M1.tuple[3] <= M2.tuple[3]);
	}

	public static bool operator>=(Matrix2f M1, Matrix2f M2) {
	    return (M1.tuple[0] >= M2.tuple[0] &&
		    M1.tuple[1] >= M2.tuple[1] &&
		    M1.tuple[2] >= M2.tuple[2] &&
		    M1.tuple[3] >= M2.tuple[3]);
	}

	public override bool Equals(object value) {
	    if(value == null) return false;
	    if (GetType() == value.GetType()) return this == (Matrix2f)value;
	    
	    return false;
	}

	public override int GetHashCode() {
	    return tuple.GetHashCode();
	}

	// operators
	public static Matrix2f operator+(Matrix2f M1, Matrix2f M2) {
	    return new Matrix2f(M1.tuple[0] + M2.tuple[0],
				M1.tuple[1] + M2.tuple[1],
				M1.tuple[2] + M2.tuple[2],
				M1.tuple[3] + M2.tuple[3]);
	}

	public static Matrix2f operator-(Matrix2f M1, Matrix2f M2) {
	    return new Matrix2f(M1.tuple[0] - M2.tuple[0],
				M1.tuple[1] - M2.tuple[1],
				M1.tuple[2] - M2.tuple[2],
				M1.tuple[3] - M2.tuple[3]);
	}

	public static Matrix2f operator*(Matrix2f M1, Matrix2f M2) {
	    return new Matrix2f(M1.tuple[0] * M2.tuple[0] + M1.tuple[1] * M2.tuple[2],
				M1.tuple[0] * M2.tuple[1] + M1.tuple[1] * M2.tuple[3],
				M1.tuple[2] * M2.tuple[0] + M1.tuple[3] * M2.tuple[2],
				M1.tuple[0] * M2.tuple[1] + M1.tuple[3] * M2.tuple[3]);
	}

	public static Matrix2f operator*(Matrix2f M, float Scalar) {
	    return new Matrix2f(M.tuple[0] * Scalar,
				M.tuple[1] * Scalar,
				M.tuple[2] * Scalar,
				M.tuple[3] * Scalar);
	}

	public static Matrix2f operator/(Matrix2f M, float Scalar) {
	    if(Scalar != 0.0f) {
		float reciproke = 1.0f/Scalar;

		return new Matrix2f(M.tuple[0] * reciproke,
				    M.tuple[1] * reciproke,
				    M.tuple[2] * reciproke,
				    M.tuple[3] * reciproke);
	    }
	    else {
		return new Matrix2f(float.MaxValue,
				    float.MaxValue,
				    float.MaxValue,
				    float.MaxValue);
	    }
	}

	public static Matrix2f operator-(Matrix2f M) {
	    return new Matrix2f(-M.tuple[0],
				-M.tuple[1],
				-M.tuple[2],
				-M.tuple[3]);
	}

	public Matrix2f Transpose() {
	    return new Matrix2f(tuple[0],
				tuple[2],
				tuple[1],
				tuple[3]);
	}

	private float[] tuple;
    }

    public class Matrix2d {

	// Constructors

        // create identity matrix
	public Matrix2d() {
	    tuple = new double[4];
	    MakeIdentity();
	}

	// copy constructor
	public Matrix2d(Matrix2d M) {
	    tuple = new double[4];
	    tuple[0] = M.tuple[0];
	    tuple[1] = M.tuple[1];
	    tuple[2] = M.tuple[2];
	    tuple[3] = M.tuple[3];
	}

	// construct from double points
	public Matrix2d(double M00, double M01, double M10, double M11) {
	    tuple = new double[4];
	    tuple[0] = M00;
	    tuple[1] = M01;
	    tuple[2] = M10;
	    tuple[3] = M11;
	}

	// construct from vectors
	// colums means:
	// true: vectors are colums of the matrix
	// false: vectors are rows of the matrix
	public Matrix2d(Vector2f V1, Vector2f V2, bool colums) {
	    tuple = new double[4];
	    if(colums) {
		tuple[0] = V1.X;
		tuple[1] = V2.X;
		tuple[2] = V1.Y;
		tuple[3] = V2.Y;		
	    }
	    else {
		tuple[0] = V1.X;
		tuple[1] = V1.Y;
		tuple[2] = V2.X;
		tuple[3] = V2.Y;		
	    }
	}

	// create a diagonal matrix
	public Matrix2d(double M00, double M11) {
	    tuple = new double[4];

	    MakeDiagonal(M00, M11);
	}

	// create a rotation matrix (positive angle - counterclockwise)
	public Matrix2d(double Angle) {
	    tuple = new double[4];

	    FromAngle(Angle);
	}

	public void MakeZero() {
	    tuple[0] = 0.0;
	    tuple[1] = 0.0;
	    tuple[2] = 0.0;
	    tuple[3] = 0.0;
	}

	public void MakeIdentity() {
	    tuple[0] = 1.0;
	    tuple[1] = 0.0;
	    tuple[2] = 0.0;
	    tuple[3] = 1.0;
	}

	public void MakeDiagonal(double M00, double M11) {
	    tuple[0] = M00;
	    tuple[1] = 0.0;
	    tuple[2] = 0.0;
	    tuple[3] = M11;
	}

	public void FromAngle(double Angle) {
	    tuple[0] = System.Math.Cos(Angle);
	    tuple[2] = System.Math.Sin(Angle);
	    tuple[1] = -tuple[2];
	    tuple[3] = tuple[0];
	}

       // accessors
	public double[] Matrix2 {
	    get {
		return tuple;
	    }

	    set {
		tuple = value;
	    }
	}

	public double Get(short Column, short Row) {
	    return tuple[Column + 2 * Row];
	}

	public void Set(double Value, short Column, short Row) {
	    tuple[Column + 2 * Row] = Value;
	}

	public Vector2d GetRow(short Row) {
	    int i0 = 2*Row;
	    return new Vector2d(tuple[i0], tuple[i0+1]);
	}

	public void SetRow(short Row, Vector2f V) {
	    int i0 = 2*Row;
	    tuple[i0] = V.X;
	    tuple[i0+1] = V.Y;
	}

	// comparison
	public static bool operator==(Matrix2d M1, Matrix2d M2) {
	    return M1.tuple == M2.tuple;
	}

	public static bool operator!=(Matrix2d M1, Matrix2d M2) {
	    return M1.tuple != M2.tuple;
	}

	public static bool operator<(Matrix2d M1, Matrix2d M2) {
	    return (M1.tuple[0] < M2.tuple[0] &&
		    M1.tuple[1] < M2.tuple[1] &&
		    M1.tuple[2] < M2.tuple[2] &&
		    M1.tuple[3] < M2.tuple[3]);
	}

	public static bool operator>(Matrix2d M1, Matrix2d M2) {
	    return (M1.tuple[0] > M2.tuple[0] &&
		    M1.tuple[1] > M2.tuple[1] &&
		    M1.tuple[2] > M2.tuple[2] &&
		    M1.tuple[3] > M2.tuple[3]);
	}

	public static bool operator<=(Matrix2d M1, Matrix2d M2) {
	    return (M1.tuple[0] <= M2.tuple[0] &&
		    M1.tuple[1] <= M2.tuple[1] &&
		    M1.tuple[2] <= M2.tuple[2] &&
		    M1.tuple[3] <= M2.tuple[3]);
	}

	public static bool operator>=(Matrix2d M1, Matrix2d M2) {
	    return (M1.tuple[0] >= M2.tuple[0] &&
		    M1.tuple[1] >= M2.tuple[1] &&
		    M1.tuple[2] >= M2.tuple[2] &&
		    M1.tuple[3] >= M2.tuple[3]);
	}

	public override bool Equals(object value) {
	    if(value == null) return false;
	    if (GetType() == value.GetType()) return this == (Matrix2d)value;
	    
	    return false;
	}

	public override int GetHashCode() {
	    return tuple.GetHashCode();
	}

	// operators
	public static Matrix2d operator+(Matrix2d M1, Matrix2d M2) {
	    return new Matrix2d(M1.tuple[0] + M2.tuple[0],
				M1.tuple[1] + M2.tuple[1],
				M1.tuple[2] + M2.tuple[2],
				M1.tuple[3] + M2.tuple[3]);
	}

	public static Matrix2d operator-(Matrix2d M1, Matrix2d M2) {
	    return new Matrix2d(M1.tuple[0] - M2.tuple[0],
				M1.tuple[1] - M2.tuple[1],
				M1.tuple[2] - M2.tuple[2],
				M1.tuple[3] - M2.tuple[3]);
	}

	public static Matrix2d operator*(Matrix2d M1, Matrix2d M2) {
	    return new Matrix2d(M1.tuple[0] * M2.tuple[0] + M1.tuple[1] * M2.tuple[2],
				M1.tuple[0] * M2.tuple[1] + M1.tuple[1] * M2.tuple[3],
				M1.tuple[2] * M2.tuple[0] + M1.tuple[3] * M2.tuple[2],
				M1.tuple[0] * M2.tuple[1] + M1.tuple[3] * M2.tuple[3]);
	}

	public static Matrix2d operator*(Matrix2d M, double Scalar) {
	    return new Matrix2d(M.tuple[0] * Scalar,
				M.tuple[1] * Scalar,
				M.tuple[2] * Scalar,
				M.tuple[3] * Scalar);
	}

	public static Matrix2d operator/(Matrix2d M, double Scalar) {
	    if(Scalar != 0.0f) {
		double reciproke = 1.0/Scalar;

		return new Matrix2d(M.tuple[0] * reciproke,
				    M.tuple[1] * reciproke,
				    M.tuple[2] * reciproke,
				    M.tuple[3] * reciproke);
	    }
	    else {
		return new Matrix2d(double.MaxValue,
				    double.MaxValue,
				    double.MaxValue,
				    double.MaxValue);
	    }
	}

	public static Matrix2d operator-(Matrix2d M) {
	    return new Matrix2d(-M.tuple[0],
				-M.tuple[1],
				-M.tuple[2],
				-M.tuple[3]);
	}

	public Matrix2d Transpose() {
	    return new Matrix2d(tuple[0],
				tuple[2],
				tuple[1],
				tuple[3]);
	}

	private double[] tuple;
    }


}
